package acn.spring.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import acn.spring.backend.springDataJpa.CategoryRepository;
import acn.spring.backend.springDataJpa.CustomerRepository;
import acn.spring.backend.springDataJpa.OrderRepository;
import acn.spring.backend.springDataJpa.ProductOrderRepository;
import acn.spring.backend.springDataJpa.ProductRepository;
import acn.spring.backend.springDataJpa.UserRepository;
import acn.spring.domain.Category;
import acn.spring.domain.Customer;
import acn.spring.domain.Order;
import acn.spring.domain.Product;
import acn.spring.domain.ProductOrder;
import acn.spring.service.ShopService;

@Service
public class ShopServiceImpl implements ShopService {

	private ProductRepository productRepository;
	private OrderRepository orderRepository;
	private CustomerRepository customerRepository;
	private CategoryRepository categoryRepository;
	private ProductOrderRepository productOrderRepository;
	private UserRepository userRepository;

	@Autowired
	public ShopServiceImpl(ProductRepository productRepository, OrderRepository orderRepository,
			CustomerRepository customerRepository, CategoryRepository categoryRepository,
			ProductOrderRepository productOrderRepository, UserRepository userRepository) {
		this.productRepository = productRepository;
		this.orderRepository = orderRepository;
		this.customerRepository = customerRepository;
		this.categoryRepository = categoryRepository;
		this.productOrderRepository = productOrderRepository;
		this.userRepository = userRepository;
	}

	@Override
	@Transactional(readOnly = true)
	public Category getCategory(String name) {
		return categoryRepository.findByName(name);
	}

	@Override
	@Transactional(readOnly = true)
	public List<Category> getAllCategories() {
		return categoryRepository.findAll();
	}

	@Override
	public List<Product> getProducts(String name) {
		return productRepository.findByName(name);
	}

	@Override
	public void saveProduct(Product product) {
		productRepository.saveAndFlush(product);
	}

	@Override
	public void deleteAllProducts() {
		productRepository.deleteAllInBatch();
	}

	@Override
	@Secured("ROLE_ADMIN")
	public Product addProduct(Product product) {
		product.setStar(0);
		product.setFeedbackCount(0);
		return productRepository.save(product);
	}

	@Override
	@Transactional(readOnly = true)
	public List<Product> getAllProducts() {
		return productRepository.findAll();
	}

	@Override
	@Transactional(readOnly = true)
	public Page<Product> getAllProducts(Pageable pageable) {
		return productRepository.findAll(pageable);
	}

	@Override
	@Transactional(readOnly = true)
	public Product getProduct(long id) {
		return productRepository.findOne(id);
	}

	@Override
	public void deleteProduct(long id) {
		productRepository.delete(id);
	}

	@Override
	public List<ProductOrder> getProductOrders(Product product) {
		return productOrderRepository.findByPkProduct(product);
	}

	@Override
	public List<ProductOrder> getAllProductOrders() {
		return productOrderRepository.findAll();
	}

	@Override
	public List<Order> getAllOrders() {
		return orderRepository.findAll();
	}

	@Override
	public Order addOrder(Order order) {
		return orderRepository.save(order);
	}

	@Override
	public Order getOrder(long id) {
		return orderRepository.findOne(id);
	}

	@Override
	public void saveOrder(Order order) {
		orderRepository.saveAndFlush(order);
	}

	@Override
	public Customer getCustomer(long id) {
		return customerRepository.findOne(id);
	}

	@Override
	@Transactional(readOnly = true)
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		return userRepository.findByUsername(username);
	}

	@Override
	public List<Product> findTopFiveBestSeller() {
		return productRepository.findTopFiveBestSeller();
	}

	@Override
	@Transactional(readOnly = true)
	public Page<Product> findBestSellerProductsByCategory(Category category, Pageable pageable) {

		Page<Object[]> page = productRepository.findBestSellerProductsByCategory(category, pageable);

		if (!page.hasContent()) {
			// no sold product yet
			return productRepository.findByCategory(category, pageable);
		}

		List<Product> productList = new ArrayList<Product>();
		for (Object[] objectArray : page.getContent()) {
			productList.add((Product) objectArray[0]);
		}

		return new PageImpl<Product>(productList, pageable, page.getTotalElements());
	}

	@Override
	@Transactional(readOnly = true)
	public Page<Product> getProducts(Category category, Pageable pageable) {
		Page<Product> page = null;

		org.springframework.data.domain.Sort.Order orderForBestseller = pageable.getSort().getOrderFor("bestseller");
		org.springframework.data.domain.Sort.Order orderForRating = pageable.getSort().getOrderFor("rating");

		if (orderForBestseller != null) {
			pageable = new PageRequest(pageable.getPageNumber(), pageable.getPageSize());
			page = findBestSellerProductsByCategory(category, pageable);
		} else {
			if (orderForRating != null) {
				Sort sort = new Sort(Direction.DESC, "star", "feedbackCount");
				pageable = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), sort);
			}
			page = productRepository.findByCategory(category, pageable);
		}

		return page;
	}
}
